package com.admin.service.impl;

import com.admin.common.exception.BizException;
import com.admin.service.OssUtilsService;
import com.aliyun.oss.*;
import com.aliyun.oss.internal.OSSHeaders;
import com.aliyun.oss.model.*;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 阿里OSS
 */
@Slf4j
@Service
public class OssUtilsServiceImpl implements OssUtilsService {


    @Value("${oss.endPoint}")
    private String END_POINT;
    @Value("${oss.accessKeyId}")
    private String ACCESS_KEY_ID;
    @Value("${oss.accessKeySecret}")
    private String ACCESS_KEY_SECRET;
    @Value("${oss.bucketName}")
    private String BUCKET_NAME;
    @Value("${oss.ossPath}")
    private String OSS_PATH;
    @Value("${oss.ossPathChild}")
    private String OSS_PATH_CHILD;
    @Value("${oss.ossUrl}")
    private String OSS_URL;


    public String getOssUrl() {
        return OSS_URL;
    }

    public String getOssPathChild() {
        return OSS_PATH_CHILD;
    }

    @Override
    public String getSignUrl(String objectName) {
        if (StringUtils.isEmpty(objectName) || !objectName.contains(BUCKET_NAME)) {
            return objectName;
        }
        objectName = objectName.replace(OSS_URL, "");
        OSS ossClient = new OSSClientBuilder().build(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 设置签名URL过期时间，单位为毫秒。
            Date expiration = new Date(new Date().getTime() + 3600 * 1000);
            // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
            URL url = ossClient.generatePresignedUrl(BUCKET_NAME, objectName, expiration);
            return url.toString();
        } catch (OSSException oe) {
           log.error("getSignUrl 获取签名后的url:{}, 异常：{}", objectName, oe);
           throw new BizException(-1, "获取签名url异常,请联系管理员!");
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 获取防盗链列表
     * @return
     */
    @Override
    public List<String> getRefererList() {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 获取存储空间Referer白名单列表。
            BucketReferer br = ossClient.getBucketReferer(BUCKET_NAME);
            List<String> refererList = br.getRefererList();
            return refererList;
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            throw new BizException(-1, oe.getMessage());
        }finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 删除防盗链
     * @return
     */
    @Override
    public Boolean delReferer() {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 防盗链不能直接清空，需要新建一个允许空Referer的规则来覆盖之前的规则。
            BucketReferer br = new BucketReferer();
            ossClient.setBucketReferer(BUCKET_NAME, br);
            return true;
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.error("Request ID:" + oe.getRequestId());
            log.error("Host ID:" + oe.getHostId());
            throw new BizException(-1, "清空防盗链异常:"+ oe);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }


    @Override
    public Boolean setReferer(List<String> list) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            List<String> refererList = new ArrayList<String>();
            // 添加Referer白名单。Referer参数支持通配符星号（*）和问号（？）。
            for (String s : list) {
                if (StringUtils.isEmpty(s)) {
                    continue;
                }
                refererList.add(s);
            }
            // 设置存储空间Referer列表。设为true表示Referer字段允许为空，设为false表示Referer字段不允许为空。
            BucketReferer br = new BucketReferer(false, refererList);
            ossClient.setBucketReferer(BUCKET_NAME, br);
            return true;
        } catch (OSSException oe) {
            log.error("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.", oe);
            throw new BizException(-1, "设置防盗链异常:"+ oe);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    @Override
    public String getUpdateUrl(String url) {
        if (StringUtils.isEmpty(url)) {
            return "";
        }
        //旧的地址 - 直接返回
        if (!url.contains(BUCKET_NAME)) {
            return url;
        }
        String[] urlArray = url.split("\\?");
        return urlArray[0];
    }

    /**
     * 上传文件
     *
     * @param file
     * @param name
     * @return
     * @throws IllegalStateException
     * @throws IOException
     */
    public void upload(MultipartFile file, String name) throws IOException {
        OSSClient ossClient = null;
        try {
            ossClient = new OSSClient(END_POINT, ACCESS_KEY_ID,ACCESS_KEY_SECRET);
            if (ossClient.doesBucketExist(BUCKET_NAME)) {
            } else {
                ossClient.createBucket(BUCKET_NAME);
            }
            setBucketPublicReadable(ossClient, OSS_PATH);
            uploadFile(ossClient, OSS_PATH, OSS_PATH_CHILD + name, file);
        } catch (IOException e) {
            e.printStackTrace();
            throw new IOException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    @Override
    public void uploadNewVersion(MultipartFile file, String name) throws IOException {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
        try {
            // 如果需要上传时设置存储类型和访问权限，请参考以下示例代码。
             ObjectMetadata metadata = new ObjectMetadata();
             metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
             metadata.setObjectAcl(CannedAccessControlList.Private);
             metadata.setContentLength(file.getSize());
            // 可以在metadata中标记文件类型
            //设置Content-disposition的内容模板格式，基于base64编码格式
             String downloadFileNameTemplate = "attachment; filename=\"=?UTF8?B?%s?=\"";
            //对真正文件名称进行base64编码
             String base64FileName = new String(Base64.getEncoder().encode(file.getOriginalFilename().getBytes(StandardCharsets.UTF_8)));
            //设置下载文件的名称
            metadata.setContentDisposition(String.format(downloadFileNameTemplate, base64FileName));

             ossClient.putObject(BUCKET_NAME, OSS_PATH_CHILD + name, file.getInputStream(), metadata);
        } catch (Exception oe) {
            log.error("uploadNewVersion, error:{}", oe);
            throw new BizException(-1, oe.getMessage());
        }  finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

    /**
     * 上传文件
     * 
     * @param client
     * @param bucketName
     * @param key
     * @param file
     * @throws OSSException
     * @throws ClientException
     * @throws IOException
     */
    private static void uploadFile(OSSClient client, String bucketName, String key, MultipartFile file)
        throws OSSException, ClientException, IOException {
        ObjectMetadata objectMeta = new ObjectMetadata();
        objectMeta.setContentLength(file.getSize());
        // 可以在metadata中标记文件类型
        // objectMeta.setContentType("image/jpeg");
        //设置Content-disposition的内容模板格式，基于base64编码格式
        String downloadFileNameTemplate = "attachment; filename=\"=?UTF8?B?%s?=\"";
        //对真正文件名称进行base64编码
        String base64FileName = new String(Base64.getEncoder().encode(file.getOriginalFilename().getBytes(StandardCharsets.UTF_8)));
        //设置下载文件的名称
        objectMeta.setContentDisposition(String.format(downloadFileNameTemplate, base64FileName));
        //上传文件
        getContentType(file.getOriginalFilename());
        client.putObject(bucketName, key, file.getInputStream(), objectMeta);
    }

    /**
     * 创建Bucket.
     *
     * @param client
     * @param bucketName
     * @throws OSSException
     * @throws ClientException
     */
    @SuppressWarnings("unused")
	private void ensureBucket(OSSClient client, String bucketName) throws OSSException, ClientException {
        try {
            // 创建bucket
            client.createBucket(bucketName);
        } catch (Exception e) {

        }
    }

    /**
     * 删除一个Bucket和其中的Objects
     *
     * @param client
     * @param bucketName
     * @throws OSSException
     * @throws ClientException
     */
    public void deleteBucket(OSSClient client, String bucketName) throws OSSException, ClientException {
        ObjectListing ObjectListing = client.listObjects(bucketName);
        List<OSSObjectSummary> listDeletes = ObjectListing.getObjectSummaries();
        for (int i = 0; i < listDeletes.size(); i++) {
            String objectName = listDeletes.get(i).getKey();
            // 如果不为空，先删除bucket下的文件
            client.deleteObject(bucketName, objectName);
        }
        client.deleteBucket(bucketName);
    }

    /**
     * 把Bucket设置为所有人可读
     *
     * @param client
     * @param bucketName
     * @throws OSSException
     * @throws ClientException
     */
    public static void setBucketPublicReadable(OSSClient client, String bucketName)
        throws OSSException, ClientException {
        // 创建bucket
        client.createBucket(bucketName);
        // 设置bucket的访问权限，public-read-write权限
        client.setBucketAcl(bucketName, CannedAccessControlList.PublicRead);
    }

    // 上传文件流
    public static void uploadThumbnail(OSSClient client, String bucketName, String key, InputStream inputStream,
        int size) throws OSSException, ClientException, IOException {
        ObjectMetadata objectMeta = new ObjectMetadata();
        objectMeta.setContentLength(size);
        // 可以在metadata中标记文件类型
        objectMeta.setContentType("image/jpeg");
        client.putObject(bucketName, key, inputStream, objectMeta);
    }


    /**
     * 下载文件
     *
     * @param client
     * @param bucketName
     * @param key
     * @param filename
     * @throws OSSException
     * @throws ClientException
     */
    public static void downloadFile(OSSClient client, String bucketName, String key, String filename)
        throws OSSException, ClientException {
        client.getObject(new GetObjectRequest(bucketName, key), new File(filename));
    }

    /**
     * 通过文件名判断并获取OSS服务文件上传时文件的contentType
     *
     * @param fileName 文件名
     * @return 文件的contentType
     */
    public static final String getContentType(String fileName) {
        String fileExtension = fileName.substring(fileName.lastIndexOf("."));
        // ----------------------------------常见图片格式-------------------------------------
        if (".bmp".equalsIgnoreCase(fileExtension)) {
            return "image/bmp";
        }
        if (".pdf".equalsIgnoreCase(fileExtension)) {
            return "application/pdf";
        }
        if (".gif".equalsIgnoreCase(fileExtension)) {
            return "image/gif";
        }
        if (".jpeg".equalsIgnoreCase(fileExtension) || ".jpg".equalsIgnoreCase(fileExtension)
            || ".png".equalsIgnoreCase(fileExtension)) {
            return "image/jpeg";
        }
        // ----------------------------------常见视频格式-------------------------------------
        if (".mp4".equalsIgnoreCase(fileExtension)) {
            return "video/mpeg4";
        }
        if (".avi".equalsIgnoreCase(fileExtension)) {
            return "video/x-msvideo";
        }
        if (".flv".equalsIgnoreCase(fileExtension)) {
            return "video/x-flv";
        }
        if (".mpg".equalsIgnoreCase(fileExtension)) {
            return "video/mpg";
        }
        if (".rmvb".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.rn-realmedia-vbr";
        }
        if (".rm".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.rn-realmedia";
        }
        if (".wma".equalsIgnoreCase(fileExtension)) {
            return "audio/x-ms-wma";
        }
        if (".mov".equalsIgnoreCase(fileExtension)) {
            return "video/quicktime";
        }
        if (".wmv".equalsIgnoreCase(fileExtension)) {
            return "video/x-ms-wmv";
        }
        // ----------------------------------其它一些格式-------------------------------------
        if (".html".equalsIgnoreCase(fileExtension)) {
            return "text/html";
        }
        if (".txt".equalsIgnoreCase(fileExtension)) {
            return "text/plain";
        }
        if (".vsd".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.visio";
        }
        if (".ppt".equalsIgnoreCase(fileExtension) || ".pptx".equalsIgnoreCase(fileExtension)) {
            return "application/vnd.ms-powerpoint";
        }
        if (".doc".equalsIgnoreCase(fileExtension) || ".docx".equalsIgnoreCase(fileExtension)) {
            return "application/msword";
        }
        if (".xml".equalsIgnoreCase(fileExtension)) {
            return "text/xml";
        }
        if (".xlsx".equalsIgnoreCase(fileExtension)) {
            return "text/xlsx";
        }
        if (".xls".equalsIgnoreCase(fileExtension)) {
            return "text/xls";
        }
        return null;
    }






    public static void main(String[] args) {
        //构建一个阿里云客户端，用于发起请求。
        //设置调用者（RAM用户或RAM角色）的AccessKey ID和AccessKey Secret。
        DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", "LTAI5t7rScdcDaTLVw8e3Qkn", "nMB56Q1mbeDh5RojYYTCcw9xg1A4QO");
        IAcsClient client = new DefaultAcsClient(profile);

        //发起请求，并得到响应。
        try {
            OSS ossClient = new OSSClientBuilder().build("https://oss-cn-shanghai.aliyuncs.com", "LTAIsnZj5dx9X3X1", "A9SOee77HVcpDKQKtqY5l9S5zVpuyM");

            try {
                // 设置签名URL过期时间，单位为毫秒。
                Date expiration = new Date(new Date().getTime() + 3600 * 1000);
                // 生成以GET方法访问的签名URL，访客可以直接通过浏览器访问相关内容。
                String objectName = "uploads/45631714-e3ea-4eb4-9eb1-108a50175bac.png";
                URL url = ossClient.generatePresignedUrl("bluesky-68324107-1178", objectName, expiration);
                System.out.println(url);
            } catch (OSSException oe) {
                System.out.println("Caught an OSSException, which means your request made it to OSS, "
                        + "but was rejected with an error response for some reason.");
                System.out.println("Error Message:" + oe.getErrorMessage());
                System.out.println("Error Code:" + oe.getErrorCode());
                System.out.println("Request ID:" + oe.getRequestId());
                System.out.println("Host ID:" + oe.getHostId());
            } catch (ClientException ce) {
                System.out.println("Caught an ClientException, which means the client encountered "
                        + "a serious internal problem while trying to communicate with OSS, "
                        + "such as not being able to access the network.");
                System.out.println("Error Message:" + ce.getMessage());
            } finally {
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }



        } catch (Exception e) {
            e.printStackTrace();

        }






    }

}
